package com.iussoft.tdengine.db.impl;

import com.iussoft.tdengine.datasource.TdengineException;
import com.iussoft.tdengine.datasource.MultipleDataSource;
import com.iussoft.tdengine.datasource.TDDatasource;
import com.iussoft.tdengine.db.BaseDao;
import com.iussoft.tdengine.util.DataMap;
import com.iussoft.tdengine.util.PropertiesConfig;
import com.iussoft.tdengine.util.StringUtil;
import com.iussoft.tdengine.util.TDengineUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @description: 默认TDengine数据库操作
 * @author: Hou Dayu
 * @date: Created in 2020/11/10
 */
@Service("defaultTDengineDao")
public class DefaultTDengineDaoImpl implements BaseDao {

    private Logger log = LoggerFactory.getLogger("tdengine logs");

    @Autowired
    private Environment env;

    private MultipleDataSource multipleDataSource;

    @Bean(name = "multipleDataSource",initMethod = "afterPropertiesSet")
    public MultipleDataSource multipleDataSource() {
        PropertiesConfig.setApplicatinConfig(env);//加载配置文件
        TDengineUtil.setTddbCacheName(PropertiesConfig.getApplicatinConfig("tdengine.datasource.cache.space"));
        if(null == multipleDataSource) {
            String ifOpenSync = StringUtil.doNull(PropertiesConfig.getApplicatinConfig("tdengine.enabled"), "0");//是否启用
            if ("0".equals(ifOpenSync)) {
                return null;
            }
            MultipleDataSource dataSource = new MultipleDataSource();
            if (StringUtil.isNotNull(PropertiesConfig.getApplicatinConfig("tdDatasource.url")) && StringUtil.isNotNull(PropertiesConfig.getApplicatinConfig("tdDatasource.username"))) {
                TDDatasource tdDatasource = new TDDatasource(PropertiesConfig.getApplicatinConfig("tdDatasource.url"),PropertiesConfig.getApplicatinConfig("tdDatasource.username"),PropertiesConfig.getApplicatinConfig("tdDatasource.passowrd"));
                dataSource.setTdDatasource(tdDatasource);
            }
            dataSource.setDatasourceKeys(PropertiesConfig.getApplicatinConfig("tdengine.datasourcekeys"));
            if (StringUtil.isNotNull(PropertiesConfig.getApplicatinConfig("tdengine.initalSzie"))) {
                dataSource.setInitialSize(Integer.parseInt(PropertiesConfig.getApplicatinConfig("tdengine.initalSzie")));
            }
            if (StringUtil.isNotNull(PropertiesConfig.getApplicatinConfig("tdengine.maxActive"))) {
                dataSource.setMaxActive(Integer.parseInt(PropertiesConfig.getApplicatinConfig("tdengine.maxActive")));
            }
            if (StringUtil.isNotNull(PropertiesConfig.getApplicatinConfig("tdengine.maxWate"))) {
                dataSource.setMaxWate(Integer.parseInt(PropertiesConfig.getApplicatinConfig("tdengine.maxWate")));
            }
            if (StringUtil.isNotNull(PropertiesConfig.getApplicatinConfig("tdengine.minIdle"))) {
                dataSource.setMinIdle(Integer.parseInt(PropertiesConfig.getApplicatinConfig("tdengine.minIdle")));
            }
            multipleDataSource = dataSource;
        }
        return multipleDataSource;
    }

    @Override
    public String defDatasourceKey() {
        return multipleDataSource.getDatasourceKey();
    }

    @Override
    public boolean execute(String datasourceKey, String sql) {
        try {
           boolean mark = executeSql(multipleDataSource.getConnection(datasourceKey),sql);
            log.info("待执行sql={},结果：{}",sql,mark);
            return mark;
        } catch (SQLException e) {
            log.error("infoType = error; message = {}; errorMessage = {}",sql,"异常信息：" + e.getMessage());
            throw new TdengineException("执行sql:(" + sql + ")出错；异常信息：" + e.getMessage());
        }
    }

    @Override
    public boolean existsTable(String datasourceKey, String tableName) {
        Connection connection =null;
        Statement stmt = null;
        ResultSet rs =null;
        try{
            connection = multipleDataSource.getConnection(datasourceKey);
            stmt = connection.createStatement();
            rs = stmt.executeQuery("show tables like '"+tableName+"'");
            while (rs.next()) {
                return true;
            }
        } catch (SQLException e) {
            throw new TdengineException("检查表("+tableName+")是否存在失败；异常信息：" + e.getMessage());
        } finally {
            close(connection,stmt,rs);
        }
        return false;
    }

    private Pattern pattern = Pattern.compile("select (.*?) from");

    @Override
    public Map<String, Object> selectPage(String datasourceKey, String sql, String orderBy, int pageIndex, int pageSize) {
        try{
            int start = (pageIndex - 1) * pageSize;
            StringBuilder strBuf = new StringBuilder();
            String orderByStr = "";
            if (StringUtil.isNotNull(orderBy)) {
                orderByStr = " order by " + orderBy;
            }
            strBuf.append(sql + orderByStr + " LIMIT " + pageSize + " OFFSET " + start);
            Connection connection = multipleDataSource.getConnection(datasourceKey);
            log.info("待查询sql={}",strBuf.toString());
            Matcher m = pattern.matcher(sql);
            String countSql = "";
            if(m.find()) {
                countSql = sql.replaceAll(m.group(1), " count(*) ");
            } else {
                countSql = "select count(*) "+sql.substring(sql.indexOf(" from "),sql.length());
            }
            int total = selectCount(connection, countSql);
            List<Map> list = select(connection, strBuf.toString(),"");
            Map<String, Object> data = new HashMap();
            data.put("total", total);
            data.put("rows", list);
            return data;
        } catch (SQLException e) {
            log.error("infoType = error; message = {}; errorMessage = {}",sql,"异常信息：" + e.getMessage());
            throw new TdengineException("执行sql:(" + sql + ")出错；异常信息：" + e.getMessage());
        }
    }

    @Override
    public List<Map> select(String datasourceKey, String sql, String orderBy) {
        try{
            if(StringUtil.isNotNull(orderBy)){
                sql = sql +"order by "+orderBy;
            }
            log.info("待查询sql={}",sql);
            List<Map> list = select(multipleDataSource.getConnection(datasourceKey), sql,"");
            return list;
        } catch (SQLException e) {
            log.error("infoType = error; message = {}; errorMessage = {}",sql,"异常信息：" + e.getMessage());
            throw new TdengineException("执行sql:(" + sql + ")出错；异常信息：" + e.getMessage());
        }
    }

    @Override
    public Map<String, Object> selectPage(String datasourceKey, String sql, String keyPrefix, String orderBy, int pageIndex, int pageSize) {
        try{
            int start = (pageIndex - 1) * pageSize;
            StringBuilder strBuf = new StringBuilder();
            String orderByStr = "";
            if (StringUtil.isNotNull(orderBy)) {
                orderByStr = " order by " + orderBy;
            }
            strBuf.append(sql + orderByStr + " LIMIT " + pageSize + " OFFSET " + start);
            Connection connection = multipleDataSource.getConnection(datasourceKey);
            log.info("待查询sql={}",strBuf.toString());
            Matcher m = pattern.matcher(sql);
            String countSql = "";
            if(m.find()) {
                countSql = sql.replaceAll(m.group(1), " count(*) ");
            } else {
                countSql = "select count(*) "+sql.substring(sql.indexOf(" from "),sql.length());
            }
            int total = selectCount(connection, countSql);
            List<Map> list = select(connection, strBuf.toString(),keyPrefix);
            Map<String, Object> data = new HashMap();
            data.put("total", total);
            data.put("rows", list);
            return data;
        } catch (SQLException e) {
            log.error("infoType = error; message = {}; errorMessage = {}",sql,"异常信息：" + e.getMessage());
            throw new TdengineException("执行sql:(" + sql + ")出错；异常信息：" + e.getMessage());
        }
    }

    @Override
    public List<Map> select(String datasourceKey, String sql, String keyPrefix, String orderBy) {
        try{
            if(StringUtil.isNotNull(orderBy)){
                sql = sql +"order by "+orderBy;
            }
            log.info("待查询sql={}",sql);
            List<Map> list = select(multipleDataSource.getConnection(datasourceKey), sql,keyPrefix);
            return list;
        } catch (SQLException e) {
            log.error("infoType = error; message = {}; errorMessage = {}",sql,"异常信息：" + e.getMessage());
            throw new TdengineException("执行sql:(" + sql + ")出错；异常信息：" + e.getMessage());
        }
    }

    private List<Map> select(Connection connection,String sql,String keyPrefix) throws SQLException{
        Statement stmt = null;
        ResultSet rs =null;
        List<Map> list = new ArrayList();
        try {
            stmt = connection.createStatement();
            rs = stmt.executeQuery(sql);
            ResultSetMetaData rsmd = rs.getMetaData();
            int count = rsmd.getColumnCount();
            while (rs.next()) {
                DataMap<String> fn = new DataMap();
                for (int i = 1; i <= count; i++) {
                    String key = rsmd.getColumnLabel(i);
                    fn.put(keyPrefix+key, rs.getString(i));
                }
                list.add(fn);
            }
        } finally {
            close(connection,stmt,rs);
        }
        return list;
    }

    private boolean executeSql(Connection connection,String sql) throws SQLException{
        Statement stmt = null;
        boolean mark = true;
        try {
            stmt = connection.createStatement();
            mark = stmt.execute(sql);
        } finally {
            close(connection,stmt,null);
        }
        return mark;
    }

    /**
     * 获取总记录条数
     * @param conn
     * @param sql
     * @return
     */
    private int selectCount(Connection conn, String sql) throws SQLException{
        ResultSet rs = null;
        Statement stmt = null;//stmt用于发送sql语句到数据库并执行sql语句
        int count = 0;
        try {
            stmt = conn.createStatement();
            rs = stmt.executeQuery(sql);
            if (rs.next()) {
                count = rs.getInt(1);
            }
        } finally {
            close(null, stmt, rs);
        }
        return count;
    }

    private void close(Connection con, Statement stmt, ResultSet rs) {
        try {
            if (null != rs)
                rs.close();
            if (null != stmt)
                stmt.close();
        } catch (SQLException e) {
            log.error("关闭连接出错；异常信息：{}",e.getMessage());
        } finally {
            try {
                if (null != con)
                    con.close();
            } catch (SQLException e) {
            }
        }
    }
}
